home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Audio-DSP / NU / Source / NuText.m < prev    next >
Encoding:
Text File  |  1993-02-28  |  15.0 KB  |  543 lines

  1. #import "NuText.h"
  2. #import <stdlib.h>
  3. #import "WorkspaceManager.h"
  4. #import "ClassManager.h"
  5. #import "NuWraps.h"
  6. #import "NuString.h"
  7. #import "MenuManager.h"
  8. #import "PrefsManager.h"
  9. #import <strings.h>
  10. #import <appkit/publicWraps.h>
  11. #import <appkit/Scroller.h>
  12. #import <appkit/ScrollView.h>
  13. #import <appkit/Application.h>
  14. #import <appkit/drag.h>
  15. #import <appkit/Pasteboard.h>
  16. #import <appkit/NXBitmapImageRep.h>
  17. #import <appkit/NXHelpPanel.h>
  18. #import <appkit/NXBrowser.h>
  19. #import <defaults/defaults.h>
  20. #import "NuGraphicCell.h"
  21. #import <appkit/Pasteboard.h>
  22. #import <appkit/tiff.h>
  23. #import <dpsclient/wraps.h>
  24. #import <ctype.h>
  25. #import <libc.h>
  26. #import <objc/objc-load.h>
  27.  
  28.  
  29. extern id Nu ;
  30. extern char bigBuf[] ; // a "big" buffer for general use
  31.  
  32. #define ESC 27
  33. #define CTRL_A (1)
  34. #define CTRL_B (2)
  35. #define CTRL_C (3)
  36. #define CTRL_D (4)
  37. #define CTRL_E (5)
  38. #define CTRL_F (6)
  39. #define CTRL_K (11)
  40. #define CTRL_N (14)
  41. #define CTRL_P (16)
  42. #define CTRL_V (22)
  43. //
  44. // following procedures were "stolen" from
  45. // Lee boynton's LispExample
  46. /*
  47. ** NXBGetc - a text stream macro to get the previous character.
  48. */
  49.  
  50. typedef struct {
  51.     id text;
  52.     NXTextBlock *block;
  53. } textInfo;
  54.  
  55. static char getPrevious(NXStream *s)
  56. {
  57.     textInfo *info = (textInfo *) s->info;
  58.  
  59.     NXTextBlock *block = info->block->prior;
  60.     if (!block) {
  61.     return EOF;
  62.     }
  63.     s->buf_base = block->text;
  64.     s->buf_ptr = s->buf_base + block->chars;
  65.     s->offset -= block->chars;
  66.     info->block = block;
  67.     return *(--s->buf_ptr);
  68. }
  69.  
  70. #define NXBGetc(s) \
  71.     (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
  72.     *(--((s)->buf_ptr)) )
  73.  
  74.  
  75. int
  76. findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos)
  77. { int count = 1;
  78.   char ch;
  79.   NXStream *s = [text stream];
  80.   NXSeek(s, curPos, NX_FROMSTART);
  81.   while ((ch = NXBGetc(s)) != EOF)
  82.   { if(ch == thatChar && !(--count))
  83.       return NXTell(s);
  84.     else if (ch == thisChar)
  85.       count++;
  86.   }
  87.   return -1;
  88. }
  89.  
  90. char *
  91. nextLine(char *text,char *lineBuf)
  92. { // fill lineBuf with a copy of the next line in text,
  93.   // not including any terminating newLine.  Return NULL if
  94.   // no line found, else return address of first character
  95.   // following line 
  96.   char *cursor = text ;
  97.   int i = 0 ;
  98.   if(*cursor == '\0')
  99.     return NULL ;
  100.   while(*cursor &&  *cursor != '\n')
  101.   { lineBuf[i++] = *cursor ;
  102.     cursor++ ;
  103.   }
  104.   lineBuf[i] = '\0' ;
  105.   if(*cursor == '\n')
  106.     return ++cursor ;
  107.   else
  108.     return cursor ;
  109. }
  110.  
  111. int
  112. findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos)
  113. {
  114.     int count = 1;
  115.     char ch;
  116.     NXStream *s = [text stream];
  117.     NXSeek(s, curPos, NX_FROMSTART);
  118.     while ((ch = NXGetc(s)) != EOF) {
  119.     if (ch == thatChar && !(--count))
  120.         return NXTell(s);
  121.     else if (ch == thisChar)
  122.         count++;
  123.     }
  124.     return -1;
  125. }
  126.  
  127. void highlightChar(id text, int charPosition)
  128. {
  129.     NXSelPt selStart, selEnd;
  130.     [text getSel:&selStart :&selEnd];
  131.     [text setSel:charPosition :charPosition+1];
  132.     [[text window] flushWindow];
  133.     NXPing();
  134.     [[text window] disableFlushWindow];
  135.     [text setSel:selStart.cp :selEnd.cp];
  136.     [[text window] reenableFlushWindow];
  137. }
  138.  
  139.  
  140. @implementation NuText:Text
  141.  
  142. + initialize ;
  143. { // super will register a bunch of directives
  144.   [super initialize] ;
  145.   // here we "re-register" the NeXTGraphic directive to make
  146.   // NuGraphicCells (a subclass of the undocumented
  147.   // NXGraphicCell) look after the NeXTGraphic RTFD directive. 
  148.   [[self class] registerDirective: "NeXTGraphic" forClass:[NuGraphicCell class]] ;
  149.   return self ;
  150. }
  151.  
  152. - awake ;
  153. { // wait until everything has been unarchived, then do setup stuff
  154.   [super awake] ;
  155.   [self perform:@selector(awakeAfterDelay:) 
  156.     with:self  afterDelay:1 cancelPrevious:YES];
  157.   return self ;
  158. }
  159.  
  160. - awakeAfterDelay: sender ;
  161. { const char *pBoardTypes[] = {NXFilenamePboardType,NULL} ;
  162.   int pBoardTypeCount = 1 ; // elements in types
  163.   if([window isKindOf: [WorkspaceManager class]])
  164.   { [self setEditable: YES] ;
  165.     [self setGraphicsImportEnabled: YES] ;
  166.     // register to accept icons
  167.     [self registerForDraggedTypes:(const char *const *)pBoardTypes
  168.                  count:pBoardTypeCount] ;
  169.   }
  170.   else 
  171.   { [self setEditable: NO] ;
  172.   } 
  173.   return self ;
  174. }
  175.  
  176.  
  177. - evalSelection:sender
  178. { // compile and run the workspace selection
  179.   // Define default name of class:
  180. #define NUEVAL "NuEval"
  181.   char tmpFile[] = NUEVAL, fullPath[128] ;
  182.   NXSelPt start, end ;
  183.   FILE *fp ;
  184.   int textLen  ;
  185.   char *headerBuf, *trueBuf, *textBuf, *cursor ;
  186.   BOOL understandsMessage ;
  187.  
  188.   understandsMessage = [window respondsTo: @selector(message:)] ;
  189.   if(understandsMessage)
  190.     [window message: "Evaluating..."] ;
  191.   // get the selected text
  192.   [self getSel:&start :&end] ;
  193.   textLen = end.cp-start.cp ;
  194.   textBuf = trueBuf = (char *) malloc(textLen+1) ; 
  195.   [self getSubstring:textBuf start:start.cp length:textLen] ;
  196.   textBuf[textLen] = '\0' ;
  197.   // now find out if we have any header
  198.   headerBuf = textBuf ;
  199.   while(isspace(*headerBuf))
  200.     headerBuf++ ;  // scan past any initial whiteSpace
  201.   if(*headerBuf == '#') // then we have a header
  202.   { char messBuf[256] ;
  203.     cursor = headerBuf ;
  204.     while(cursor = nextLine(cursor,bigBuf))
  205.     {  // check for a load command
  206.        if(!strncmp(bigBuf,"#pragma load ",13))
  207.        { if(understandsMessage)
  208.          { sprintf(messBuf,"Preloading %s",&bigBuf[13]) ;
  209.            [window message: messBuf] ;
  210.          }
  211.          [Nu loadIfNeeded: &bigBuf[13]] ;
  212.       }
  213.        else if(!strlen(bigBuf))
  214.          break ;
  215.     }
  216.     if(cursor == NULL)
  217.     { // No body to evaluate      free(textBuf) ;
  218.       if(understandsMessage)
  219.        [window message: ""] ;
  220.       return self ;
  221.     }
  222.     textBuf = cursor ;
  223.     cursor-- ;
  224.     *cursor = '\0' ; // chop string into 2 pieces    
  225.   }
  226.   else
  227.     cursor = headerBuf = "" ;   // no header at all
  228.  
  229.   // now hack together the file...
  230.   sprintf(fullPath,"/tmp/%s.m",tmpFile) ;
  231.   fp = fopen(fullPath,"w+") ;
  232.   fprintf(fp,
  233.    "%s\n"
  234.    "#import \"Glyph.h\"\n"
  235.    "#import \"Nutation.h\"\n"
  236.    "@interface %s: Glyph\n"
  237.    "+(void) doit ;\n"
  238.    "@end\n\n"
  239.    "@implementation %s\n"
  240.    "+ (void) doit\n"
  241.    "{\n"
  242.    "#line 1\n"
  243.    "%s\n"
  244.    "}\n"
  245.    "@end",
  246.    headerBuf, tmpFile, tmpFile, textBuf) ;
  247.   fflush(fp) ;
  248.   fclose(fp) ;
  249.   
  250.   // free up alloc'ed memory
  251.   if(headerBuf[0] != '\0') // string was cut into 2 pieces...
  252.     *cursor = ' ' ;   // reassemble
  253.   free(trueBuf) ; // trueBuf always points to beginning of malloc'ed string
  254.  
  255.   // Now compile and load the file
  256.   if(understandsMessage)
  257.     [window message: "Compiling..."] ;
  258.   if([ClassManager compileClass: NUEVAL file: fullPath])
  259.   { // compile succeeded, load it
  260.     if(understandsMessage)
  261.       [window message: "Loading..."] ;
  262.     if([ClassManager load: NUEVAL])
  263.     { // load succeeded, execute it
  264.       if(understandsMessage)
  265.         [window message: "Executing..."] ;
  266.       objc_msgSend(objc_getClass(tmpFile),
  267.          sel_getUid("doit")) ;
  268.      if(understandsMessage)
  269.         [window message: ""] ;
  270.       // now remove the temporary class
  271.       [ClassManager unload: tmpFile] ;
  272.     }
  273.   }
  274.   unlink(tmpFile) ;
  275.   return self ;
  276. }
  277.  
  278.  
  279. - insertLink: sender ;
  280. { [window setDocEdited: YES] ;
  281.   return [self replaceSelWithCell: [[NXLinkCell alloc] initForView: self]] ;
  282. }
  283.  
  284. - insertMarker: sender ;
  285. { [window setDocEdited: YES] ;
  286.   return [self replaceSelWithCell: [[NXMarkerCell alloc] initForView: self]] ;
  287. }
  288.  
  289. - keyDown:(NXEvent *)theEvent
  290. {  // provide some emacs-style key bindings
  291.     int i ;
  292.     static unsigned short val = '\0', lastChar = '\0' ;
  293.     NXSelPt selStart, selEnd;
  294.     [self getSel:&selStart :&selEnd];
  295.     lastChar = val ; // val was set in "previous" call of keyDown..
  296.     val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
  297.     switch (val) {
  298.     case ESC: // don't want ESC to cause a beep
  299.         return self ;
  300.     case '<':
  301.         if(lastChar == ESC)
  302.         { [self setSel: 1 :1] ;
  303.           [self scrollSelToVisible] ;
  304.           return self ;
  305.         }
  306.         break ;
  307.     case '>':
  308.         if(lastChar == ESC)
  309.         { i = [self textLength] ; --i ;
  310.           [self setSel: i :i] ;
  311.           [self scrollSelToVisible] ;
  312.           return self ;
  313.         }
  314.         break ;
  315.     case ')':
  316.         i = findMatchingOpenParen(self,')','(',selEnd.cp);
  317.         [super keyDown:theEvent];
  318.         if (i >= 0)
  319.         highlightChar(self,i);
  320.         return self;
  321.     case '}':
  322.         i = findMatchingOpenParen(self,'}','{',selEnd.cp);
  323.         [super keyDown:theEvent];
  324.         if (i >= 0)
  325.         highlightChar(self,i);
  326.         return self;
  327.     case ']':
  328.         i = findMatchingOpenParen(self,']','[',selEnd.cp);
  329.         [super keyDown:theEvent];
  330.         if (i >= 0)
  331.         highlightChar(self,i);
  332.         return self;
  333.     case CTRL_A: // beginning of line
  334.             i = [self lineFromPosition:selStart.cp] ;
  335.         i = [self positionFromLine: i] ;
  336.         [self setSel:i :i];
  337.         return self;
  338.     case CTRL_E: // end of line
  339.         i = [self lineFromPosition: selStart.cp] ;
  340.         i = [self positionFromLine: i + 1] ; // beginning of next line
  341.         [self setSel:i -1 :i - 1];
  342.         return self;
  343.     case CTRL_B:// backwards one char
  344.         if (selStart.cp > 1)
  345.         [self setSel:selStart.cp - 1 :selStart.cp -1];
  346.         else
  347.         NXBeep();
  348.         return self;
  349.     case CTRL_F: // forwards one char
  350.         if (selEnd.cp < [self textLength])
  351.             [self setSel:selEnd.cp+1 :selEnd.cp+1];
  352.         else
  353.         NXBeep();
  354.         return self;
  355.     case CTRL_D: // delete char under cursor
  356.         [self setSel:selEnd.cp :selEnd.cp+1];
  357.         [self replaceSel:""];
  358.         [self setSel:selStart.cp :selStart.cp] ;
  359.         return self;
  360.     case CTRL_K: // clear to end of line 
  361.         i = [self lineFromPosition: selStart.cp] ;
  362.         i = [self positionFromLine: i + 1] ; // beginning of next line
  363.         [self setSel:selStart.cp :--i];
  364.         [self replaceSel:""];
  365.         [self setSel:selStart.cp :selStart.cp] ;
  366.         return self;
  367.      case CTRL_P: // move directly up one line...fake down arrow
  368.         theEvent->data.key.charCode = 173 ;
  369.         theEvent->data.key.charSet = 1 ;
  370.         theEvent->flags = NX_NUMERICPADMASK ;
  371.         break ;
  372.     case CTRL_N: // move directly down one line...fake up arrow
  373.         theEvent->data.key.charCode = 175 ;
  374.         theEvent->data.key.charSet = 1 ;
  375.         theEvent->flags = NX_NUMERICPADMASK ;
  376.         break ;
  377.     case 'v':    // move up one screenful
  378.         if(lastChar != ESC)
  379.         break ;
  380.     case CTRL_V: // move down one screenful
  381.         { id scroller ;
  382.           NXRect aRect ;
  383.           NXEvent fakeEvent ;
  384.           fakeEvent = *theEvent ;
  385.           fakeEvent.type = NX_MOUSEDOWN ;
  386.           fakeEvent.data.mouse.reserved = 18 ;
  387.           fakeEvent.data.mouse.eventNum = 9999 ;
  388.           fakeEvent.data.mouse.click = 1 ;
  389.           // fakeEvent.data.mouse.unused = 0 ;
  390.           fakeEvent.flags = NX_ALTERNATEMASK ; 
  391.           scroller = [[superview superview] vertScroller] ;
  392.           [scroller calcRect:&aRect forPart:
  393.                   val == 'v'? NX_DECLINE :NX_INCLINE ] ;
  394.           [scroller convertPoint: &aRect.origin toView: nil] ;
  395.           fakeEvent.location = aRect.origin ;
  396.               DPSPostEvent(&fakeEvent,1) ;
  397.           fakeEvent.type = NX_MOUSEUP ;
  398.           fakeEvent.time += 10 ; // 10 /68ths of a second
  399.               DPSPostEvent(&fakeEvent,0) ;
  400.           return self ;
  401.         }
  402.     default:
  403.         break;
  404.     }
  405.     return [super keyDown:theEvent];
  406. }
  407.  
  408.  
  409. - mouseDown: (NXEvent *) anEvent ;
  410. { // augment double-click behavior to
  411.   // check for bracketed structures and quoted
  412.   // items. augment command-click behaviour to
  413.   // select between backquotes.
  414.   NXSelPt start, end ;
  415.   char openers[] = "{([<\"'/`" ;
  416.   char closers[] = "})]>\"'/`" ;
  417.   char c,d, *place ;
  418.   int otherPos ;
  419.   NXStream *aStream ;
  420.   [super mouseDown: anEvent] ;
  421.   if(anEvent->data.mouse.click == 2)
  422.   { aStream = [self stream] ;
  423.     [self getSel: &start :&end] ;
  424.     if(start.cp > 0)
  425.     { // NXSeek(aStream,start.cp-1,NX_FROMSTART) ; // worked in 2.0, changed
  426.       NXSeek(aStream,start.cp,NX_FROMSTART) ; // to this for 3.0!
  427.       c = NXGetc(aStream) ;
  428.       if(place = index(closers,c)) // we have right side of a structure
  429.       {  d = openers[(int) place - (int) closers] ;// grab matching char
  430.          if( (otherPos = findMatchingOpenParen(self, c, d, start.cp -1))
  431.            != -1)
  432.         { if(c == '`') // put selection "inside" ` ... ` pair
  433.             [self setSel: otherPos + 1 :start.cp - 1] ;
  434.           else
  435.             [self setSel: otherPos :start.cp + 1] ;
  436.           return self ;
  437.         }
  438.       }
  439.       else
  440.       { NXSeek(aStream,start.cp,NX_FROMSTART) ;
  441.         c = NXGetc(aStream) ;
  442.         if(place = index(openers,c)) // we have a structure
  443.         { d = closers[(int) place - (int) openers] ;
  444.           if( (otherPos = findMatchingCloseParen(self, c, d, start.cp +1))
  445.            != -1)
  446.           { if(c == '`') // put selection "inside" ` ... ` pair
  447.              [self setSel: start.cp +1 :otherPos-1] ;
  448.            else
  449.              [self setSel: start.cp :otherPos] ;
  450.           }
  451.         }
  452.       }
  453.     }
  454.   }
  455.   else if((anEvent->data.mouse.click == 1) &&
  456.      (anEvent->flags & NX_COMMANDMASK))
  457.   { // command click == search back for backquote (or beginning of text)
  458.     // forward for backquote (or end ot text), then select. 
  459.     int from, to ;
  460.     aStream = [self stream] ;
  461.     [self getSel: &start :&end] ;
  462.     if(start.cp > 0)
  463.     { from = start.cp - 1 ;
  464.       NXSeek(aStream,from,NX_FROMSTART) ;
  465.       while(from >= 1 && ((c = NXBGetc(aStream)) != '`'))
  466.         from-- ;
  467.       to = start.cp ;
  468.       NXSeek(aStream,to,NX_FROMSTART) ;
  469.       while(!NXAtEOS(aStream) && ((c = NXGetc(aStream)) != '`'))
  470.         to++ ;
  471.       [self setSel: from :to] ;
  472.     }
  473.   }
  474.   return self ;
  475. }
  476.  
  477. #define NX_NOBUTTONS (0)
  478. #define NXSetWindowLevel _NXSetWindowLevel
  479.  
  480. - pasteScreen: sender ;
  481. { // grab a portion of the screen,
  482.   // save it as a file, then paste
  483.   // it into the rtfd
  484.   id theWin, aWin, theImage ;
  485.   char saveName[32] ;
  486.   if([(theWin = [NXApp mainWindow]) isMemberOf: [WorkspaceManager class]])
  487.   { NXRect aRect ;
  488.     extern int NXSetWindowLevel(int, int);
  489.    // get a rectangle from the user (using a procedure defined
  490.    // in NuWraps.psw-defined procedure)
  491.    grabScreen(&aRect.origin.x,&aRect.origin.y,
  492.      &aRect.size.width, &aRect.size.height) ;
  493.    // now make an invisible window to grab bits
  494.    aWin = [[Window alloc]
  495.      initContent: &aRect
  496.      style:NX_PLAINSTYLE
  497.      backing:NX_NONRETAINED
  498.       buttonMask:NX_NOBUTTONS
  499.      defer:NO] ;
  500.     PSsetautofill(NO,[aWin windowNum]) ;
  501.    NXSetWindowLevel([aWin windowNum],
  502.        NX_MAINMENULEVEL);
  503.    [aWin orderFront: 0] ;
  504.    [[aWin contentView] lockFocus] ;
  505.    theImage = [[NXImage alloc] init] ;
  506.    [theImage setDataRetained:YES] ;
  507.    aRect.origin.x = aRect.origin.y = 0.0 ; // convert screen to aWin
  508.    [theImage useRepresentation:
  509.      [[NXBitmapImageRep alloc] initData: NULL fromRect: &aRect]] ;
  510.    [[aWin contentView] unlockFocus] ;
  511.    [aWin free] ;
  512.    sprintf(saveName,"%s%x.tiff", NUSCREENPASTE, (unsigned) time(NULL));
  513.    [theImage setName: saveName] ;
  514.    [self replaceSelWithCell:[[NXGraphicCell alloc] initIconCell: saveName]] ;
  515.    [window setDocEdited: YES] ;
  516.    [self setRTFD: YES] ;
  517.    return self ;
  518.   }
  519.   else
  520.     return self ; // ignore
  521. }  
  522.  
  523.  
  524. - showMarkers: sender ;
  525. { // here I use 2 undocumented Text functions, as gleaned
  526.   // from IB...in order to control visibility or markers
  527.   if([self markersVisible])
  528.   { [self setMarkersVisible: NO] ;
  529.     [sender setStringValue: "Show Markers"] ;
  530.   }
  531.   else
  532.   { [self setMarkersVisible: YES] ;
  533.     [sender setStringValue: "Hide Markers"] ;
  534.   }
  535.   return self ;
  536. }
  537.  
  538.  
  539. - (int) tag ;
  540. { return TEXTVIEWTAG ;
  541. }
  542.  
  543. @end